﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Configuration.Install;
using System.IO;
using System.Linq;
using System.Xml;

namespace DatabaseGroup.SSIS.SourceAdapters.Setup
{
    [RunInstaller(true)]
    public partial class RedirectBinding : Installer
    {
        private const string ComponentName = "DatabaseGroup.SSIS.SourceAdapters.DelimitedFileSource2008";
        private const string ComponentPublicKey = "103567041829a1c2";
        private const string ComponentCulture = "Neutral";
        private const string ComponentOldVersion = "0.0.0.1-1.0.6.9";
        private const string ComponentNewVersion = "1.0.7.0";

        public RedirectBinding()
        {
            InitializeComponent();
        }

        public override void Install(IDictionary stateSaver)
        {
            base.Install(stateSaver);
            UpdateAllMachineConfigs();
        }

        public override void Rollback(IDictionary savedState)
        {
            base.Rollback(savedState);

            string configPath = Path.Combine(Environment.GetEnvironmentVariable("windir") ?? @"C:\windows", "Microsoft.NET");
            foreach (string configFile in Directory.GetFiles(configPath, "machine.config.del_bak", SearchOption.AllDirectories))
            {
                try
                {
                    File.Move(configFile, Path.Combine(Path.GetDirectoryName(configFile), "machine.config"));
                }
                catch
                {
                    // ignore - nothing we can do
                }
            }
        }

        private void UpdateAllMachineConfigs()
        {
            string configPath = Path.Combine(Environment.GetEnvironmentVariable("windir") ?? @"C:\windows", "Microsoft.NET");
            foreach (string configFile in Directory.GetFiles(configPath, "machine.config", SearchOption.AllDirectories))
            {
                try
                {
                    File.Copy(configFile, configFile + ".del_bak", true);
                }
                catch (Exception)
                {
                    throw new Exception("Possible permissions error? Failed to create backup of: " + configFile);
                }

                UpdateConfigBindingRedirect(configFile, ComponentName, ComponentPublicKey, ComponentCulture, ComponentOldVersion, ComponentNewVersion);
            }
        }

        private static void UpdateConfigBindingRedirect(string configFile, string name, string publicKey, string culture, string oldVersion, string newVersion)
        {
            XmlDocument configDoc = new XmlDocument();
            configDoc.Load(configFile);

            try
            {
                XmlNamespaceManager nsManager = new XmlNamespaceManager(configDoc.NameTable);
                nsManager.AddNamespace("urn", "urn:schemas-microsoft-com:asm.v1");

                XmlNode configurationNode = configDoc.SelectSingleNode(@"configuration");

                XmlNode runtimeNode = configDoc.SelectSingleNode(@"configuration/runtime");
                if (runtimeNode == null)
                    configurationNode.AppendChild(configDoc.CreateElement("runtime"));

                XmlNode assemblyBindingNode = configDoc.SelectSingleNode(@"configuration/runtime/urn:assemblyBinding", nsManager);
                if (assemblyBindingNode == null)
                    configurationNode.SelectSingleNode("runtime").AppendChild(
                        configDoc.CreateElement("", "assemblyBinding", "urn:schemas-microsoft-com:asm.v1"));

                XmlNode assemblyIdentityNode =
                    configDoc.SelectSingleNode(@"configuration/runtime/urn:assemblyBinding/urn:dependentAssembly/urn:assemblyIdentity[@name=""" + name + @"""]", nsManager);
                if (assemblyIdentityNode == null)
                {
                    assemblyBindingNode = configDoc.SelectSingleNode(@"configuration/runtime/urn:assemblyBinding", nsManager);
                    XmlNode dependentAssemblyNode = configDoc.CreateElement("", "dependentAssembly", "urn:schemas-microsoft-com:asm.v1");
                    assemblyIdentityNode = configDoc.CreateElement("", "assemblyIdentity", "urn:schemas-microsoft-com:asm.v1");
                    ((XmlElement)assemblyIdentityNode).SetAttribute("name", name);
                    ((XmlElement)assemblyIdentityNode).SetAttribute("publicKeyToken", publicKey);
                    ((XmlElement)assemblyIdentityNode).SetAttribute("culture", culture);
                    XmlNode bindingRedirectNode = configDoc.CreateElement("", "bindingRedirect", "urn:schemas-microsoft-com:asm.v1");
                    ((XmlElement)bindingRedirectNode).SetAttribute("oldVersion", oldVersion);
                    ((XmlElement)bindingRedirectNode).SetAttribute("newVersion", newVersion);
                    dependentAssemblyNode.AppendChild(assemblyIdentityNode);
                    dependentAssemblyNode.AppendChild(bindingRedirectNode);
                    assemblyBindingNode.AppendChild(dependentAssemblyNode);
                }
                else
                {
                    XmlNode bindingRedirectNode = assemblyIdentityNode.ParentNode.SelectSingleNode("urn:bindingRedirect", nsManager);
                    bindingRedirectNode.Attributes["oldVersion"].Value = oldVersion;
                    bindingRedirectNode.Attributes["newVersion"].Value = newVersion;
                }

                configDoc.Save(configFile);
            }
            catch
            {
                throw new Exception("Failed to update machine configuration.");
            }
        }
    }
}
